/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package coalescent.cef.phylo;

import coalescent.phylogeny.GeneTree;

import com.google.common.base.Function;
import com.google.common.collect.Collections2;

import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.visualization.layout.ObservableCachingLayout;
import edu.uci.ics.jung.visualization.layout.PersistentLayout;
import edu.uci.ics.jung.visualization.util.Caching;
import edu.uci.ics.jung.visualization.util.ChangeEventSupport;

import static coalescent.phylogeny.GeneTree.Edge;
import static coalescent.phylogeny.GeneTree.Node;

import java.awt.*;
import java.awt.geom.Point2D;
import java.io.*;
import java.util.*;

/**
 * Implementation of PersistentLayout.
 * Defers to another layout until 'restore' is called,
 * then it uses the saved vertex locations
 *
 * @author Tom Nelson
 *
 *
 */
public final class JUNG_PLayoutImpl_NodeLabel
        extends ObservableCachingLayout<GeneTree.Node, GeneTree.Edge>
        implements PersistentLayout<Node, Edge>, ChangeEventSupport, Caching {

    private final GeneTree geneTree;

    /** persisted points for vertices; init in restore() or persist */
    protected Map<String, Point> map;

    /**
     * a collection of Vertices that should not move
     */
    protected Set<Node> no_move;

    /**
     * whether the graph is locked (stops the VisualizationViewer rendering thread)
     */
    protected boolean locked;

    /**
     * create an instance with a passed layout
     * create containers for graph components
     * @param geneTree
     * @param layout
     */
    public JUNG_PLayoutImpl_NodeLabel(GeneTree geneTree, Layout<Node, Edge> layout) {

        super(layout);

        this.geneTree = geneTree;
        this.no_move  = new HashSet<>();
    }

    /**
     * This method calls <tt>initialize_local_vertex</tt> for each vertex, and
     * also adds initial coordinate information for each vertex. (The vertex's
     * initial location is set by calling <tt>initializeLocation</tt>.
     */
    protected void initializeLocations() {

        for (Node v : getGraph().getVertices()) {

            Point2D point = delegate.transform(v);

            if (!no_move.contains(v)) initializeLocation(v, point, getSize());
        }
    }

    /**
     * Sets persisted location for a vertex within the dimensions of the space.
     * If the vertex has not been persisted, sets a random location. If you want
     * to initialize in some different way, override this method.
     *
     * @param v
     * @param coord
     * @param d
     */
    protected void initializeLocation(Node v, Point2D coord, Dimension d) {

        Point point = map.get(v.getLabel());

        coord.setLocation(point.x, point.y);
    }

    /**
     * save the Vertex locations to a file
     * @param fileName the file to save to
     * @throws an IOException if the file cannot be used
     */
    public void persist(String fileName) throws IOException {

        map = new HashMap<>();

        for (Node v : getGraph().getVertices()) {

            Point p = new Point(transform(v));

            map.put(v.getLabel(), p);
        }

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName));

        oos.writeObject(map);
        oos.close();
    }

    /**
     * Restore the graph Vertex locations from a file
     * @param fileName the file to use
     * @throws java.io.IOException for file problems
     * @throws ClassNotFoundException for classpath problems
     */
    @SuppressWarnings("unchecked")
    public void restore(String fileName) throws IOException, ClassNotFoundException {

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
        Map map1              = (Map) ois.readObject();


        // check if all vertices are present
        Collection<String> nodes = Collections2.transform(geneTree.getFreq().keySet(),
                                       new Function<Node, String>() {

            @Override
            public String apply(coalescent.phylogeny.GeneTree.Node node) {
                return node.getLabel();
            }

        });

        if (!map1.keySet().containsAll(nodes))
            throw new IOException("some nodes missing. corrupted layout");

        map = map1;

        ois.close();
        initializeLocations();

        locked = true;

        fireStateChanged();
    }

    public void lock(boolean locked) {
        this.locked = locked;
    }

    /*
     * (non-Javadoc)
     *
     * @see edu.uci.ics.jung.visualization.Layout#incrementsAreDone()
     */
    public boolean done() {
        return super.done() || locked;
    }

    /*
     * (non-Javadoc)
     *
     * @see edu.uci.ics.jung.visualization.Layout#lockVertex(edu.uci.ics.jung.graph.Vertex)
     */
    public void lock(Node v, boolean state) {

        no_move.add(v);
        delegate.lock(v, state);
    }
}
